home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / MacCalendar 1.0d5 / Src / MacCalendar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-30  |  19.7 KB  |  679 lines  |  [TEXT/KAHL]

  1. /*                                    MacCalendar.c                                    */
  2. /*
  3.  * MacCalendar.c
  4.  * Copyright © 1993-94 Apple Computer Inc. All rights reserved.
  5.  * Based on the Status Bar Sample.c by Steve Christensen.
  6.  *
  7.  *    File Type            sdev
  8.  *    File Creator        SCAL    -- registered with DTS
  9.  *    Resource Type        sdev
  10.  *    Resource ID            0
  11.  *    Resource Attributes    purgeable
  12.  *
  13.  * Other options (MetroWerks, Think C 7.0):
  14.  *    Set Require Prototypes, Check Pointer Types, All other warnings.
  15.  *    Do not set trigraph recognition.
  16.  *    Enable Apple extensions.
  17.  * MetroWerks link/project options:
  18.  *    Link single segment
  19.  *    Set Project type "Code Segment", Standard Header.
  20.  */
  21. #ifndef  SystemSevenOrLater
  22. #define  SystemSevenOrLater    1
  23. #endif
  24.  
  25. #include <Fonts.h>
  26. #include <Memory.h>
  27. #include <Menus.h>
  28. #include <Quickdraw.h>
  29. #include <Resources.h>
  30. #include <ToolUtils.h>
  31. #include <Types.h>
  32. #include <Windows.h>
  33. #include <Icons.h>
  34. #include "ControlStrip.h"
  35. #include "MacCalendar.h"
  36. /*
  37.  * Metrowerks uses A4 to reference globals. The A4-setup code was copied from the
  38.  * WDEF.c sample included in the Metrowerks DR3 distribution. MPW and Think C
  39.  * use PC-relative addressing in a single-segment code module.
  40.  */
  41. #ifdef __MWERKS__
  42. #include "A4Stuff.h"    //    also included in <MacHeaders>
  43. #include "SetupA4.h"    //    required to handle callback functions
  44. #endif
  45.  
  46. /*
  47.  * Define the patterns as C-strings so they can be addressed as constants
  48.  * within the program.
  49.  */
  50. #define kWhitePattern        ((ConstPatternParam) "\000\000\000\000\000\000\000\000")
  51. #define kBlackPattern        ((ConstPatternParam) "\377\377\377\377\377\377\377\377")
  52. #define pstrcpy(dst, src)    (BlockMove((src), (dst), (src)[0] + 1))
  53. #define width(rect)            ((rect).right - (rect).left)
  54. #define height(rect)        ((rect).bottom - (rect).top)
  55.  
  56. /*
  57.  * This record defines the information we need to draw the calendar. It is initialized
  58.  * when we are called with the sdevInitModule message, and passed to and from the
  59.  * Status Bar manager.
  60.  */
  61. typedef struct GlobalRecord {
  62.     Handle            iconSuite;                    /* Status bar icon                    */
  63.     Handle            textStrings;                /* Balloon help string etc.            */
  64.     StringHandle    dateStringHandle;            /* The date string preference        */
  65.     PicHandle        rightArrowPicture;            /* Popup arrow                        */
  66.     short            fontNumber;                    /* Preference: display font         */
  67.     short            fontSize;                    /* Preference: display font size    */
  68.     short            firstDayOfWeek;                /* Sunday == 1                        */
  69. } GlobalRecord, *GlobalPtr, **GlobalHandle;
  70. /*
  71.  * We access the global information through a macro. For example, GLOBAL.iconSuite is
  72.  * the icon suite handle in the global record.
  73.  */
  74. #define GLOBAL            (*globalPtr)
  75. #define PicFrame(what)    ((**GLOBAL.what).picFrame)    /* The picture frame rectangle    */
  76.  
  77. /*
  78.  * Define the local/global prototypes.
  79.  */
  80. pascal long                    main(
  81.         unsigned long            message,
  82.         GlobalHandle            globalHandle,
  83.         const Rect                *statusRect,
  84.         GrafPtr                    statusPort
  85.     );
  86. long                        CtlStripInitialize(void);
  87. void                        CtlStripCleanUp(
  88.         GlobalHandle            globalHandle
  89.     );
  90. long                        CtlStripPeriodicTickle(
  91.         GlobalPtr                globalPtr,
  92.         const Rect                *statusRect
  93.     );
  94. long                        CtlStripMouseClick(
  95.         GlobalPtr                globalPtr,
  96.         const Rect                *statusRect
  97.     );
  98. long                        CtlStripDrawStatusIcon(
  99.         GlobalPtr                globalPtr,
  100.         const Rect                *statusRect
  101.     );
  102. OSErr                        CtlStripSavePreferences(
  103.         GlobalPtr                globalPtr
  104.     );
  105.  
  106.  
  107. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  108.  * main
  109.  *
  110.  * This "main" program is called by the Control Strip manager.
  111.  */
  112. pascal long
  113. main(
  114.         unsigned long            message,
  115.         GlobalHandle            globalHandle,
  116.         const Rect                *statusRect,
  117.         GrafPtr                    statusPort
  118.     )
  119. {
  120. #ifdef __MWERKS__
  121.         long                    oldA4 = SetCurrentA4();
  122. #endif
  123.         short                    savedState;
  124.         register GlobalPtr        globalPtr;
  125.         long                    result;
  126.         Str255                    helpString;
  127.  
  128.         if (0) {                /* Unused variables            */
  129.             statusPort;
  130.         }
  131.         if (((long) globalHandle) != 0) {
  132.             /*
  133.              * We have already allocated the global record. Save its lock state, lock
  134.              * the handle, and get the global pointer (so we can write GLOBAL.something)
  135.              */
  136.             savedState = HGetState((Handle) globalHandle);
  137.             HLock((Handle) globalHandle);
  138.             globalPtr = *globalHandle;
  139.         }
  140.         result = 0;                                    /* Unknown message result        */
  141.         switch (message) {
  142.         case sdevInitModule:                        /* Initialize the module        */
  143.             /*
  144.              * Initialization always sets globalHandle to NULL to avoid the HSetState
  145.              * at the exit routine. If CtlStripInitialize succeeds, it sets the result
  146.              * to the global parameter.
  147.              */
  148.             globalHandle = NULL;
  149.             result = CtlStripInitialize();            /* Do the initialization and    */
  150.             break;                                    /* Return global or error code    */
  151.         case sdevCloseModule:                        /* Clean up before closing        */
  152.             CtlStripCleanUp(globalHandle);
  153.             globalHandle = NULL;
  154.             break;
  155.         case sdevFeatures:                            /* Return feature bits            */
  156.             result = (  (1<<sdevWantMouseClicks)    /* We handle mouse down            */
  157.                       | (1<<sdevDontAutoTrack)        /* We track the mouse, too        */
  158.                       | (1<<sdevHasCustomHelp)        /* Custom help string            */
  159.                     );
  160.             break;
  161.         case sdevGetDisplayWidth:                    /* Return display width            */
  162.             result = kIconWidth    + width(PicFrame(rightArrowPicture));
  163.             break;
  164.         case sdevPeriodicTickle:                    /* Nothing else is happening    */
  165.             result = CtlStripPeriodicTickle(globalPtr, statusRect);
  166.             break;
  167.         case sdevDrawStatus:                        /* Draw the status bar info        */
  168.             result = CtlStripDrawStatusIcon(globalPtr, statusRect);
  169.             break;
  170.         case sdevMouseClick:                        /* Status bar click                */
  171.             result = CtlStripMouseClick(globalPtr, statusRect);
  172.             break;
  173.         case sdevSaveSettings:                        /* Save changed settings        */
  174.             result = CtlStripSavePreferences(globalPtr);
  175.             break;
  176.         case sdevShowBalloonHelp:                    /* Display custom balloon help    */
  177.             /*
  178.              * We don't really have a custom help string, but this shows how to do it.
  179.              */
  180.             SBGetDetachedIndString(helpString, GLOBAL.textStrings, kStringHelp);
  181.             SBShowHelpString(statusRect, helpString);
  182.             break;
  183.         }
  184.         if (globalHandle != NULL)                    /* If we have globals allocated    */
  185.             HSetState((Handle) globalHandle, savedState);    /* Restore lock state    */    
  186. #ifdef __MWERKS__
  187.         SetA4(oldA4);
  188. #endif
  189.         return (result);
  190. }
  191.  
  192.  
  193. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  194.  * Initialization
  195.  */
  196. long
  197. CtlStripInitialize(void)
  198. {
  199.         register GlobalHandle    globalHandle;
  200.         register GlobalPtr        globalPtr;
  201.         long                    result;
  202.         Str255                    work;
  203.         SavedSettingsHandle        prefsHandle;
  204.         Handle                    *theIconSuite;
  205.         Boolean                    needSave;
  206.         long                    tempLong;
  207. #define PREF    (**prefsHandle)
  208.  
  209.         needSave = FALSE;
  210.         globalHandle = (GlobalHandle) NewHandleClear(sizeof (GlobalRecord));
  211.         result = noErr;
  212.         if (globalHandle == NULL)
  213.             result = MemError();
  214.         else {
  215.             HLock((Handle) globalHandle);
  216.             globalPtr = *globalHandle;
  217.             /*
  218.              * Load and detach the icon suite
  219.              */
  220.             theIconSuite = &GLOBAL.iconSuite;
  221.             result = SBGetDetachIconSuite(theIconSuite, ICON_StatusBar, svAllSmallData);
  222.         }
  223.         if (result == noErr) {
  224.             GLOBAL.textStrings = GetResource('STR#', STRN_Info);
  225.             result = ResError();
  226.         }
  227.         if (result == noErr) {
  228.             DetachResource(GLOBAL.textStrings);
  229.             GLOBAL.rightArrowPicture = GetPicture(PICT_RightArrow);
  230.             if (GLOBAL.rightArrowPicture == NULL)
  231.                 result = ResError();
  232.         }
  233.         if (result == noErr) {
  234.             DetachResource((Handle) GLOBAL.rightArrowPicture);
  235.             /*
  236.              * Get the saved preferences, if any, and configure the drawing
  237.              * environment. Note that the sample status bar doesn't dispose
  238.              * of the prefsHandle.
  239.              */
  240.             SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
  241.             result = SBLoadPreferences(work, (Handle *) &prefsHandle);
  242.             if (result == noErr
  243.              && GetHandleSize((Handle) prefsHandle) == sizeof (SavedSettings)
  244.              && PREF.signature == kApplicationCreator
  245.              && PREF.prefVersion == kPrefVersion) {
  246.                  /*
  247.                   * Use the saved preference resource
  248.                   */
  249.                 pstrcpy(work, PREF.fontName);
  250.                 GetFNum(work, &GLOBAL.fontNumber);
  251.                 GLOBAL.fontSize = PREF.fontSize;
  252.                 GLOBAL.firstDayOfWeek = PREF.firstDayOfWeek;
  253.                 pstrcpy(work, PREF.dateString);            /* This must be last        */
  254.             }
  255.             else {
  256.                 /*
  257.                  * Hmm, we don't have any preferences. Build a new preference resource.
  258.                  */
  259.                 result = noErr;
  260.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontName);
  261.                 GetFNum(work, &GLOBAL.fontNumber);
  262.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontSize);
  263.                 StringToNum(work, &tempLong);
  264.                 GLOBAL.fontSize = tempLong;
  265.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFirstDayOfWeek);
  266.                 StringToNum(work, &tempLong);
  267.                 GLOBAL.firstDayOfWeek = tempLong;
  268.                 /*
  269.                  * This must be last as we will store work in the dateStringHandle
  270.                  */
  271.                 SBGetDetachedIndString(work, GLOBAL.textStrings, kStringDayNames);
  272.                 needSave = TRUE;
  273.             }
  274.             GLOBAL.dateStringHandle = NewString(work);
  275.             if (GLOBAL.dateStringHandle == NULL)
  276.                 result = MemError();
  277.         }
  278.         /*
  279.          * We've finished all initialization. If there is an error, exit through
  280.          * CtlStripCleanUp to dispose of handles and other junk. If initialization
  281.          * are successful, unlock the handle and return the handle cast to a long.
  282.          */
  283.         if (result != noErr) {
  284.             CtlStripCleanUp(globalHandle);
  285.             result = 0;
  286.         }
  287.         else {
  288.             if (needSave)
  289.                 (void) CtlStripSavePreferences(globalPtr);
  290.             HUnlock((Handle) globalHandle);
  291.             result = (long) globalHandle;
  292.         }
  293.         return (result);
  294. #undef PREF
  295. }
  296.  
  297.  
  298. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  299.  * Termination
  300.  */
  301. void
  302. CtlStripCleanUp(
  303.         GlobalHandle            globalHandle
  304.     )
  305. {
  306.         register GlobalPtr        globalPtr;
  307.  
  308.         if ((long) globalHandle > 0) {
  309.             HLock((Handle) globalHandle);
  310.             globalPtr = *globalHandle;
  311.             if (GLOBAL.iconSuite != NULL)
  312.                 DisposeIconSuite(GLOBAL.iconSuite, TRUE);
  313.             if (GLOBAL.textStrings != NULL)
  314.                 DisposeHandle(GLOBAL.textStrings);
  315.             if (GLOBAL.rightArrowPicture != NULL)
  316.                 DisposeHandle((Handle) GLOBAL.rightArrowPicture);
  317.             if (GLOBAL.dateStringHandle != NULL)
  318.                 DisposeHandle((Handle) GLOBAL.dateStringHandle);
  319.             DisposeHandle((Handle) globalHandle);
  320.         }
  321. }
  322.  
  323.  
  324. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  325.  * Draw the icon in the status bar.
  326.  */
  327. long
  328. CtlStripDrawStatusIcon(
  329.         GlobalPtr                globalPtr,
  330.         const Rect                *statusRect
  331.     )
  332. {
  333.         Rect                    viewRect;
  334.         short                    arrowHeight;
  335.  
  336.         viewRect = *statusRect;
  337.         viewRect.right = viewRect.left + kIconWidth;
  338.         (void) PlotIconSuite(&viewRect, atNone, ttNone, GLOBAL.iconSuite);
  339.         /*
  340.          * Draw an right-arrow to show that we have a popup menu. Well, we don't
  341.          * actually have a popup menu, but we do pop up a calendar when clicked on.
  342.          */
  343.         arrowHeight = height(PicFrame(rightArrowPicture));
  344.         viewRect.left = viewRect.right;
  345.         viewRect.right += width(PicFrame(rightArrowPicture));
  346.         viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
  347.         viewRect.bottom = viewRect.top + arrowHeight;
  348.         DrawPicture(GLOBAL.rightArrowPicture, &viewRect);
  349.         return (0);
  350. }
  351.  
  352. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  353.  * Position the calendar with respect to the status bar. If there is enough room
  354.  * above, put it above, else put it below. Left and right operate similarly.
  355.  */
  356. static void
  357. GetDisplayRect(
  358.         const Rect                *statusRect,
  359.         Point                    displaySize,
  360.         Rect                    *windowRect
  361.     )
  362. {
  363.         Rect                    sBarRect;
  364.  
  365. #define kCtlStripFrame    4        /* The frame above/below the icon itself            */
  366.         sBarRect = *statusRect;
  367.         LocalToGlobal((Point *) &sBarRect.top);
  368.         LocalToGlobal((Point *) &sBarRect.bottom);
  369.         if (sBarRect.top - kCtlStripFrame - displaySize.v - (GetMBarHeight() + 2) > 0) {
  370.             /*
  371.              * The calendar is displayed above the status bar.
  372.              */
  373.             windowRect->bottom = sBarRect.top - kCtlStripFrame - 1;
  374.             windowRect->top = windowRect->bottom - displaySize.v;
  375.         }
  376.         else {
  377.             /*
  378.              * The calendar is displayed below the status bar.
  379.              */
  380.             windowRect->top = sBarRect.bottom + kCtlStripFrame + 1;
  381.             windowRect->bottom = windowRect->top + displaySize.v;
  382.         }
  383.         if (sBarRect.right - displaySize.h > 0) {
  384.             /*
  385.              * The calendar is displayed to the left of the calendar icon.
  386.              */
  387.             windowRect->right = sBarRect.right;
  388.             windowRect->left = windowRect->right - displaySize.h;
  389.         }
  390.         else {
  391.             /*
  392.              * The calendar is displayed to the right of the calendar icon.
  393.              */
  394.             windowRect->left = sBarRect.left;
  395.             windowRect->right = windowRect->left + displaySize.h;
  396.         }
  397. }
  398.  
  399. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  400.  * Build the triangular "next month" and "previous month" buttons.
  401.  */
  402. static void
  403. MakeTriangularButtons(
  404.         const Rect                *monthRect,
  405.         PolyHandle                *leftButton,
  406.         PolyHandle                *rightButton,
  407.         Rect                    *leftButtonRect,
  408.         Rect                    *rightButtonRect
  409.     )
  410. {
  411.         FontInfo                fontInfo;
  412.         short                    buttonSize;
  413.         short                    halfSize;
  414.         Rect                    bothButtonRect;
  415.  
  416.         GetFontInfo(&fontInfo);
  417.         buttonSize = (fontInfo.ascent & ~1);    /* Round down to even value        */
  418.         halfSize = buttonSize / 2;
  419.         bothButtonRect = *monthRect;
  420.         bothButtonRect.bottom -= (1 + fontInfo.leading);
  421.         bothButtonRect.top = bothButtonRect.bottom - buttonSize;
  422. #define kButtonSeparation    4                    /* 1.0d3, was 2                    */
  423.         bothButtonRect.left =
  424.             (width(*monthRect) >> 1) - buttonSize - kButtonSeparation;
  425.         bothButtonRect.right =
  426.             (width(*monthRect) >> 1) + buttonSize + kButtonSeparation;
  427.         *leftButtonRect = bothButtonRect;
  428. /* 1.0d4 +    */
  429.         leftButtonRect->right = leftButtonRect->left + halfSize;
  430.         *rightButtonRect = bothButtonRect;
  431.         rightButtonRect->left = rightButtonRect->right - halfSize;
  432.         *leftButton = OpenPoly();
  433.             MoveTo(halfSize, 0);
  434.             LineTo(halfSize, buttonSize);
  435.             LineTo(0, halfSize);
  436.             LineTo(halfSize, 0);
  437.         ClosePoly();
  438.         OffsetPoly(*leftButton, leftButtonRect->left, leftButtonRect->top);
  439.         *rightButton = OpenPoly();
  440.             MoveTo(0, 0);
  441.             LineTo(halfSize, halfSize);
  442.             LineTo(0, buttonSize);
  443.             LineTo(0, 0);
  444.         ClosePoly();
  445.         OffsetPoly(*rightButton, rightButtonRect->left, rightButtonRect->top);
  446. /* 1.0d4 -    */
  447. }
  448.  
  449. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  450.  * Now that we've done setup, do the actual mouse tracking. If the mouse hits the
  451.  * right button, advance the month and draw it. If it hits the left button, draw
  452.  * the previous month.
  453.  */
  454. static void
  455. DrawCalendarAndTrackMouse(
  456.         GlobalPtr                globalPtr,
  457.         WindowPtr                windowPtr,
  458.         const Rect                *monthRect
  459.     )
  460. {
  461.         Rect                    leftButtonRect;
  462.         Rect                    rightButtonRect;
  463.         PolyHandle                leftButton;
  464.         PolyHandle                rightButton;
  465.         unsigned long            nowSeconds;
  466.         DateTimeRec                now;
  467.         Point                    mousePt;
  468.         short                    thisYear;
  469.         short                    thisMonth;
  470.         typedef enum {
  471.             inNoButton = 0,
  472.             inLeftButton = 1,
  473.             inRightButton = 2
  474.         } WhichButton;
  475.         WhichButton                inButton;
  476.         WhichButton                wasInButton;
  477.         unsigned long            nextMonthTick;
  478.         Boolean                    redrawButtons;
  479.         Str255                    dateString;
  480.  
  481.         FrameRect(monthRect);
  482.         MakeTriangularButtons(
  483.             monthRect,
  484.             &leftButton,
  485.             &rightButton,
  486.             &leftButtonRect,
  487.             &rightButtonRect
  488.         );
  489.         pstrcpy(dateString, *GLOBAL.dateStringHandle);
  490.         GetDateTime(&nowSeconds);
  491.         Secs2Date(nowSeconds, &now);
  492.         inButton = wasInButton = inNoButton;
  493.         thisYear = thisMonth = 0;
  494.         InitCursor();
  495.         while (WaitMouseUp()) {
  496.             if (thisYear != now.year || thisMonth != now.month) {
  497.                 /*
  498.                  * Draw the new month. Also make sure the buttons are drawn.
  499.                  */
  500.                 redrawButtons = TRUE;
  501.                 EraseRect(&windowPtr->portRect);
  502.                 FrameRect(monthRect);
  503.                 switch (wasInButton) {
  504.                 case inLeftButton:
  505.                     FillPoly(leftButton, kWhitePattern);
  506.                     break;
  507.                 case inRightButton:
  508.                     FillPoly(rightButton, kWhitePattern);
  509.                     break;
  510.                 }
  511.                 FramePoly(leftButton);
  512.                 FramePoly(rightButton);
  513.                 DrawCalendar(
  514.                     now.year,
  515.                     now.month,
  516.                     GLOBAL.firstDayOfWeek,                /* Sunday                    */
  517.                     dateString,
  518.                     &windowPtr->portRect,
  519.                     GLOBAL.fontNumber,
  520.                     GLOBAL.fontSize
  521.                 );
  522.                 thisYear = now.year;
  523.                 thisMonth = now.month;
  524.             }                                            /* If drawing new month        */
  525.             /*
  526.              * Get the mouse and track it while it is in one of our buttons
  527.              */
  528.             GetMouse(&mousePt);
  529.             if (PtInRect(mousePt, &leftButtonRect))
  530.                 inButton = inLeftButton;
  531.             else if (PtInRect(mousePt, &rightButtonRect))
  532.                 inButton = inRightButton;
  533.             else {
  534.                 inButton = inNoButton;
  535.             }
  536.             if (redrawButtons || inButton != wasInButton) {
  537.                 switch (wasInButton) {
  538.                 case inLeftButton:    FillPoly(leftButton, kWhitePattern);    break;
  539.                 case inRightButton:    FillPoly(rightButton, kWhitePattern);    break;
  540.                 }
  541.                 switch (inButton) {
  542.                 case inLeftButton:    FillPoly(leftButton, kBlackPattern);    break;
  543.                 case inRightButton:    FillPoly(rightButton, kBlackPattern);    break;
  544.                 }
  545.                 FramePoly(leftButton);
  546.                 FramePoly(rightButton);
  547.                 if (inButton != wasInButton && inButton != inNoButton)
  548.                     nextMonthTick = 0;                    /* Force new month drawing    */
  549.                 redrawButtons = FALSE;
  550.                 wasInButton = inButton;
  551.             }                                            /* If button click change    */
  552.             if (inButton != inNoButton && TickCount() > nextMonthTick) {
  553.                 /*
  554.                  * The user has clicked in a button, or has held the mouse
  555.                  * down in a button for one second. Draw the appropriate month.
  556.                  */
  557.                 nextMonthTick = TickCount() + 60;
  558.                 switch (inButton) {
  559.                 case inLeftButton:
  560.                     if (--now.month <= 0) {                /* Previous month or year    */
  561.                         now.month = 12;
  562.                         --now.year;
  563.                     }
  564.                     break;
  565.                 case inRightButton:
  566.                     if (++now.month > 12) {                /* Next month or year        */
  567.                         now.month = 1;
  568.                         ++now.year;
  569.                     }
  570.                     break;
  571.                 }                                        /* Which button was clicked    */
  572.             }                                            /* Moving to a new month    */
  573.         }                                                /* Loop while mouse down    */
  574.         KillPoly(leftButton);
  575.         KillPoly(rightButton);
  576. }
  577.  
  578. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  579.  * Click in the status bar icon
  580.  */
  581. long
  582. CtlStripMouseClick(
  583.         GlobalPtr                globalPtr,
  584.         const Rect                *statusRect
  585.     )
  586. {
  587.         Rect                    windowRect;
  588.         WindowPtr                windowPtr;
  589.         GrafPtr                    savePort;
  590.         Point                    displaySize;
  591.         Rect                    monthRect;
  592.  
  593.         displaySize = GetCalendarDisplaySize(GLOBAL.fontNumber, GLOBAL.fontSize);
  594.         displaySize.h += 2;
  595.         displaySize.v += 2;
  596.         GetDisplayRect(statusRect, displaySize, &windowRect);
  597.         windowPtr = NewWindow(
  598.                     NULL,
  599.                     &windowRect,
  600.                     "\p",
  601.                     TRUE,
  602.                     plainDBox,
  603.                     (WindowPtr) -1L,
  604.                     FALSE,                                /* No go-away box            */
  605.                     0                                    /* No refCon                */
  606.                 );
  607.         if (windowPtr != NULL) {
  608.             GetPort(&savePort);
  609.             SetPort(windowPtr);
  610.             GetCalendarMonthRect(
  611.                 GLOBAL.fontNumber,
  612.                 GLOBAL.fontSize,
  613.                 &windowPtr->portRect,
  614.                 &monthRect
  615.             );
  616.             if (StillDown()) {
  617.                 /*
  618.                  * Design the two triangular buttons that will be displayed on the
  619.                  * bottom line of the calendar and create the polygons. Then draw
  620.                  * the calendar and track the mouse while it's held down.
  621.                  */
  622.                 DrawCalendarAndTrackMouse(
  623.                     globalPtr,
  624.                     windowPtr,
  625.                     &monthRect
  626.                 );
  627.             }
  628.             SetPort(savePort);
  629.             DisposeWindow(windowPtr);
  630.         }
  631.         return (0);
  632. }
  633.  
  634.  
  635. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  636.  * Save current status for restarts.
  637.  */
  638. OSErr
  639. CtlStripSavePreferences(
  640.         GlobalPtr                globalPtr
  641.     )
  642. {
  643.         OSErr                    status;
  644.         SavedSettingsHandle        prefsHandle;
  645.         Str255                    work;
  646. #define PREF    (**prefsHandle)
  647.  
  648.         prefsHandle = (SavedSettingsHandle) NewHandleClear(sizeof (SavedSettings));
  649.         status = MemError();
  650.         if (status == noErr) {
  651.             HLock((Handle) prefsHandle);
  652.             PREF.signature = kApplicationCreator;
  653.             PREF.prefVersion = kPrefVersion;
  654.             GetFontName(GLOBAL.fontNumber, PREF.fontName);
  655.             PREF.fontSize = GLOBAL.fontSize;
  656.             pstrcpy(PREF.dateString, *GLOBAL.dateStringHandle);
  657.             SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
  658.             status = SBSavePreferences(work, (Handle) prefsHandle);
  659.             DisposeHandle((Handle) prefsHandle);
  660.         }
  661.         return(status);
  662. }
  663.  
  664. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  665.  * We don't do anything interesting for periodic tickles.
  666.  */
  667. long
  668. CtlStripPeriodicTickle(
  669.         GlobalPtr                globalPtr,
  670.         const Rect                *statusRect
  671.     )
  672. {
  673.         if (0) {                /* Unused variables            */
  674.             globalPtr;
  675.             statusRect;
  676.         }
  677.         return (0);
  678. }
  679.